/**********************************
The C file for module 2

removePCB needs to call sys_free_mem. otherwise this is a memory leak

insert into correct queue in PCB priority function
**********************************/
#ifndef r2_h
#include <stdlib.h>
#include <stdio.h>
#include "mpx_supt.h"
#include "R2.H"
#include "commhand.h"
#endif

PCB_QUEUE readyQueue;
PCB_QUEUE *readyQueuePtr;
PCB_QUEUE blockedQueue;
PCB_QUEUE *blockedQueuePtr;
PCB_QUEUE suspReadyQueue;
PCB_QUEUE *suspReadyQueuePtr;
PCB_QUEUE suspBlockedQueue;
PCB_QUEUE *suspBlockedQueuePtr;


/**********************************
Author: Billy!
Edited: Brandon Strickland

This procedure searches the PCBs for a process having a specified
name. We assume process names are unique, so the first such process
may be accepted. The search should cover all PCBs currently in use,
regardless of the process state.
The character string name should be passed to this process as an
input parameter. The return value is an identifier for the PCB found,
or a null pointer if the search failed, just as for Allocate-PCB.

**********************************/
	void init_Queues(){

	readyQueuePtr = (PCB_QUEUE *)&readyQueue;
	blockedQueuePtr = &blockedQueue;
	suspReadyQueuePtr = &suspReadyQueue;
	suspBlockedQueuePtr = &suspBlockedQueue;

	readyQueue.count = 0;
	readyQueue.state = READY;
	readyQueue.head = NULL;
	readyQueue.tail = NULL;

	blockedQueue.count = 0;
	blockedQueue.state =BLOCKED;
	blockedQueue.head = NULL;
	blockedQueue.tail = NULL;

	suspReadyQueue.count = 0;
	suspReadyQueue.state = SUSPENDED_READY;
	suspReadyQueue.head = NULL;
	suspReadyQueue.tail = NULL;

	suspBlockedQueue.count = 0;
	suspBlockedQueue.state= SUSPENDED_BLOCKED;
	suspBlockedQueue.head = NULL;
	suspBlockedQueue.tail = NULL;

	
	}
/**********************************
Author: Billy!
Edited: Brandon Strickland

This procedure searches the PCBs for a process having a specified
name. We assume process names are unique, so the first such process
may be accepted. The search should cover all PCBs currently in use,
regardless of the process state.
The character string name should be passed to this process as an
input parameter. The return value is an identifier for the PCB found,
or a null pointer if the search failed, just as for Allocate-PCB.

**********************************/

PCB *findPCB(char *PCBname) {

	int i;

	PCB *curr;

	curr = readyQueue.head;

	while(curr != NULL)
	{
		if(see(curr, PCBname))
			return curr;
		curr = curr->next;
	}

	curr = blockedQueue.head;

	while(curr != NULL)
	{
		if(see(curr, PCBname))
			return curr;
		curr = curr->next;
	}

	curr = suspReadyQueue.head;

	while(curr != NULL)
	{
		if(see(curr, PCBname))
			return curr;
		curr = curr->next;
	}

	curr = suspBlockedQueue.head;

	while(curr != NULL)
	{
		if(see(curr, PCBname))
			return curr;
		curr = curr->next;
	}

	return NULL;
}

/**********************************
Author: Billy!

This is a helper function for FindPCB
**********************************/

int see(PCB *thing, char *PCBname) {
	return !strcmp(thing->name, PCBname);
}
/*
/**********************************
Author: Billy!
Edited: Wisam-Brandon Strickland

This procedure inserts a PCB into a specified queue.
The queue is specified by an identifier that could, for example,
be the address of its descriptor. Both the PCB identifier and queue
identifier are passed as input parameters. An additional parameter
is needed to indicate whether insertion is to be by priority order
or by FIFO order; both methods must be supported. Either an enumeration
type or an integer code may be used. As usual the validity of all
parameters should be checked, and an appropriate code should be
returned indicating the success or failure of the insertion.

**********************************/

int insertPCB(PCB *ins, PCB_QUEUE *que, int insType){
    PCB *curr;
    int found;
	found = 0;
	//if the queue is empty
    if(que->count == 0)
	{
	    que->tail = ins;
	    que->head = ins;
	    que->count++;
	    ins->states = que->state;
	    return found;
	}
	//else the queue isnt empty
    else
	{
		if(insType == FIFO)
		{

			ins->prev = que->tail;
			ins->prev->next = ins;
			que->tail = ins;
			que->count++;
			ins->states = que->state;
			return found;
		}
		else if(insType == PRIORITY)
		{
			curr = que->tail;
			if(que->count == 1)
			{
				if(curr->priority >= ins->priority)
				{
					que->tail->prev=ins;
					ins->next=que->tail;
					que->tail = ins;
					que->count++;
					ins->states = que->state;
				return found;
				}
				else
				{
					que->tail->next=ins;
					ins->prev = que->tail;
					que->head=ins;
					que->count++;
					ins->states = que->state;
					return found;
				}
			}
			else if(curr->priority >= ins->priority)
			{
				que->tail->next=ins;
				ins->prev=que->tail;
				que->tail = ins;
				que->count++;
				ins->states = que->state;
				return found;
			}
			else
			{
				while(curr->priority < ins->priority &&
					curr->prev->priority < ins-> priority )
				{
					curr = curr->prev;
				}
				ins->prev = curr ->prev;
				curr->prev->next=ins;
				ins->next = curr;
				curr->prev=ins;
				que->count++;
				ins->states = que->state;
				return found;
			}
		}
	}//ends if queue is empty
	return 1;
}

/**********************************
Author: Chad
Edited by: Brandon Strickland

This function creates the PCB structure
This command should allocate and setup a new PCB.
The arguments to be specified include process name,
process class (system or application), and priority.
By default the process is initially in the ready (not suspended) state.
An appropriate error message should be displayed if
the arguments are not valid or if no PCB can be allocated.

**********************************/

int createPCB(int argc, char *argv[]) {
	int test1;
	int test3;
	PCB *new;
	test1 = atoi(argv[2]);
	test3 = atoi(argv[3]);
	if(strcmp(argv[0], "createPCB") != 0)
	{
		return 0;
	}
	else if(argc != 4)
	{
		printf("Invalid createPCB command\n");
		return 1;
	}
	else if( findPCB(argv[1]) != NULL )
	{
		printf("the PCB name already exists\n");
		return 1;
	}
	else if( test1 != 0 && test1 != 1 )
	{
		printf( "Invalid process class\n");
		return 1;
	}
	else if( test3 < -128 || test3 > 127 )
	{
		printf("The priority did not lie in -128 - +127\n");
		return 1;
	}
	else
	{
		new = allocatePCB();
		strcpy( new->name,argv[1] );
		new->classs = test1;
		new->priority = test3;
		new->states = 0;
		if( !insertPCB( new, &readyQueue, READY ) )
		{
			return 1;
		}
		else
		{
			printf( "The act of inserting the PCB into the queue failed\n");
			return 1;
		}
	}
}

/**********************************
Author: Chad
Edited: Brandon Strickland

This command should deallocate an existing PCB.
Depending on your allocation strategy, the PCB may
be marked as free or its storage may be actually released.
The only argument is the process name.
An error message should be given if the specified process does not exist.

!!!!!!!!
only error checking
!!!!!!!!
**********************************/

int deletePCB(int argc, char *argv[]){
	if(strcmp(argv[0], "deletePCB") != 0)
		return 0;
	else if(argc != 2)
	{
		printf("Invalid deletePCB command\n");
		return 1;
	}
	else if( findPCB(argv[1]) == NULL )
	{
		printf("PCB not found\n");
		return 1;
	}
	else
	{
		removePCB(findPCB(argv[1]));
		return 1;
	}
}

/**********************************
Author: Chad
Edited: Brandon Strickland

This command should place a specified process in a blocked state;
its suspended status should not be changed. The process
should be removed from the ready queue, if necessary, and
inserted in the blocked queue. The only argument is the
process name. An error message should be given if the
specified process does not exist.

!!!!!!!!
This is only error checking
!!!!!!!!
**********************************/
int blockPCB(int argc, char *argv[]){
	struct PCB_t *pcbPtr;
	pcbPtr = findPCB(argv[1]);
	//checks to see if the first word is correct
	if(strcmp(argv[0], "blockPCB") != 0)
		return 0;
	//correct number of parameters?
	else if(argc != 2)
	{
		printf("Invalid blockPCB command\n");
		return 1;
	}
	//does it exist?
	else if( findPCB(argv[1]) == NULL )
	{
		printf("the PCB doesn't exist\n");
		return 1;
	}
	//is it already blocked?
	else if(pcbPtr->states == BLOCKED || pcbPtr->states == SUSPENDED_BLOCKED)
	{
		printf("the PCB is already blocked");
		return 1;
	}
	//is it in the ready queue?
	else if( pcbPtr->states == READY )
	{
		PCB *pcb;
		pcb = findPCB(argv[1]);
		helperRemove(pcb);
	//pcb->states = BLOCKED;
		insertPCB(pcb,&blockedQueue,1);
		return 1;
	}
	//is it in the suspended queue?
	else if( pcbPtr->states == SUSPENDED_READY )
	{
		PCB *pcb;
		pcb = findPCB(argv[1]);
		helperRemove(pcb);
		//pcb->states = SUSPENDED_BLOCKED;
		insertPCB(pcb,&suspBlockedQueue,1);
		return 1;
	}
	//in case we didnt think of something
	else
	{
		printf("the PCB was not in the ready queue\n");
		return 1;
	}
}

/**********************************
Author: Billy
Date: 2/24

This command should place a specified process in a
ready state; its suspended status should not be changed.
The process should be removed from the blocked queue,
if necessary, and inserted in the ready queue. The only
argument is the process name. An error message should be
given if the specified process does not exist.

Preconditions: argv contains "unblockPCB" and a valid PCB name
Postconditions: PCB is removed from blocked (susp blocked)
	queue and inserted into ready (susp ready) queue (if appropriate)

NOTE: Doesn't work yet. Needs removePCB and insertPCB implemented.
**********************************/

int unblockPCB(int argc, char *argv[]){
	struct PCB_t *pcbPtr;
	pcbPtr = findPCB(argv[1]);
	//checks to see if the first word is correct
	if(strcmp(argv[0], "unblockPCB") != 0)
		return 0;
	//correct number of parameters?
	else if(argc != 2)
	{
		printf("Invalid unblockPCB command\n");
		return 1;
	}
	//does it exist?
	else if( findPCB(argv[1]) == NULL )
	{
		printf("the PCB doesn't exist\n");
		return 1;
	}
	//is it already ready?
	else if(pcbPtr->states == READY || pcbPtr->states == SUSPENDED_READY)
	{
		printf("the PCB is already ready");
		return 1;
	}
	//is it in the blocked queue?
	else if( pcbPtr->states == BLOCKED )
	{
		PCB *pcb;
		pcb = findPCB(argv[1]);
		helperRemove(pcb);
		//pcb->states = READY;
		insertPCB(pcb,&readyQueue,1);
		return 1;
	}
	//is it in the suspended queue?
	else if( pcbPtr->states == SUSPENDED_BLOCKED )
	{
		PCB *pcb;
		pcb = findPCB(argv[1]);
		helperRemove(pcb);
		//pcb->states = SUSPENDED_READY;
		insertPCB(pcb,&suspReadyQueue,1);
		return 1;
	}
	//in case we didnt think of something
	else
	{
		printf("the PCB was not in the ready queue\n");
		return 1;
	}
}

/**********************************
Author: Chad

This command should place a specified process in a
suspended state. The state chosen will be either
suspended-ready or suspended-blocked, depending on its
previous state. If suspended processes are kept on separate
queues from non-suspended ones, then this operation may
require a queue transfer. The only argument is the process name.
An error message should be given if the specified process
does not exist.

!!!!!!!!
This is only error checking
This should be one of two suspend ones
!!!!!!!!
**********************************/

int suspendPCB(int argc, char *argv[]){
	struct PCB_t *pcbPtr;
	pcbPtr = findPCB(argv[1]);
	//checks to see if the first word is correct
	if(strcmp(argv[0], "suspendPCB") != 0)
		return 0;
	//correct number of parameters?
	else if(argc != 2)
	{
		printf("Invalid suspendPCB command\n");
		return 1;
	}
	//does it exist?
	else if( findPCB(argv[1]) == NULL )
	{
		printf("the PCB doesn't exist\n");
		return 1;
	}
	//is it already suspended?
	else if(pcbPtr->states == SUSPENDED_BLOCKED || pcbPtr->states == SUSPENDED_READY)
	{
		printf("the PCB is already suspended");
		return 1;
	}
	//is it in the ready queue?
	else if( pcbPtr->states == READY )
	{
		PCB *pcb;
		pcb = findPCB(argv[1]);
		helperRemove(pcb);
		//pcb->states = SUSPENDED_READY;
		insertPCB(pcb,&suspReadyQueue,1);
		return 1;
	}
	//is it in the blocked queue?
	else if( pcbPtr->states == BLOCKED )
	{
		PCB *pcb;
		pcb = findPCB(argv[1]);
		helperRemove(pcb);
		//pcb->states = SUSPENDED_BLOCKED;
		insertPCB(pcb,&suspBlockedQueue,1);
		return 1;
	}
	//in case we didnt think of something
	else
	{
		printf("the PCB was not in the ready queue\n");
		return 1;
	}
}

/**********************************
Author: Chad

This command should place a specified process in a
non-suspended state. The state chosen will be either ready
or blocked, depending on its previous state. If suspended
processes are kept on separate queues from non-suspended ones,
then this operation may require a queue transfer. The only
argument is the process name. An error message should be given
if the specified process does not exist.

!!!!!!!!
This is only error checking
!!!!!!!!
**********************************/

int resumePCB(int argc, char *argv[])
{
	struct PCB_t *pcbPtr;
	pcbPtr = findPCB(argv[1]);
	//checks to see if the first word is correct
	if(strcmp(argv[0], "resumePCB") != 0)
		return 0;
	//correct number of parameters?
	else if(argc != 2)
	{
		printf("Invalid resumePCB command\n");
		return 1;
	}
	//does it exist?
	else if( findPCB(argv[1]) == NULL )
	{
		printf("the PCB doesn't exist\n");
		return 1;
	}
	//is it already not suspended?
	else if(pcbPtr->states == BLOCKED || pcbPtr->states == READY)
	{
		printf("the PCB is already ready");
		return 1;
	}
	//is it in the sus_ready queue?
	else if( pcbPtr->states == SUSPENDED_READY )
	{
		PCB *pcb;
		pcb = findPCB(argv[1]);
		helperRemove(pcb);
		//pcb->states = READY;
		insertPCB(pcb,&readyQueue,1);
		return 1;
	}
	//is it in the sus_blocked queue?
	else if( pcbPtr->states == SUSPENDED_BLOCKED )
	{
		PCB *pcb;
		pcb = findPCB(argv[1]);
		helperRemove(pcb);
		//pcb->states = BLOCKED;
		insertPCB(pcb,&blockedQueue,1);
		return 1;
	}
	//in case we didnt think of something
	else
	{
		printf("the PCB could not be resumed\n");
		return 1;
	}
}

/**********************************
Author: Chad
Edited by : Wisam

This command is used to change the priority of a specified
process. The command arguments are the process name and
the desired priority value. The procedure should ensure
that the process exists, and that the desired priority is
within the acceptable range for the process class. If the process
is in a ready state, then the change of priority may require a
repositioning of the process in its queue. This can be accomplished
by removing and reinserting the PCB in priority order.

**********************************/

int setPCBPriority(int argc, char *argv[]){
	struct PCB_t *pcbPtr;
	pcbPtr = findPCB(argv[1]);
	if(strcmp(argv[0], "setPCBPriority") != 0)
	{
		return 0;
	}
	else  if ( argc != 3 )
	{
		printf("not enough or too many parameters");
		return 1;
	}
	else if( findPCB(argv[1]) == NULL )
	{
		printf("the PCB doesn't exist\n");
		return 1;
	}
	else
	{
		pcbPtr->priority = atoi(argv[2]);
		//insert into the correct queue some how
		return 1;
	}

}

/**********************************
Author: Chad

This command is used to display all information contained
in a single PCB for a process specified by name. The process
name is the only argument. If the process exists, the command
results in the display of process information on the screen.
You should define an attractive display format for the process
information. This display should present all of the significant
information in your PCB structure. If it is possible that the
display will not fit entirely on a single display screen, then
you must provide a mechanism for paging as discussed for the Help
command of Module 1.

!!!!!!!!
This is only error checking
!!!!!!!!
**********************************/

int showPCB(int argc, char *argv[])
{
	struct PCB_t *pcbPtr;
	pcbPtr = findPCB(argv[1]);
	if(strcmp(argv[0], "showPCB") != 0)
		return 0;
	else if(argc != 2)
	{
		printf("Invalid showPCB command\n");
		return 1;
	}
	else if( pcbPtr == NULL )
	{
		printf("the PCB doesn't exist\n");
		return 1;
	}
	else
	{
		showPCBs(pcbPtr);
	}
	return 1;
}

void showPCBs(PCB *pc)
{
	printf( "PCB name: %s\nclass: %d\npriority: %d\nstate: %d\n",
				pc->name, pc->classs, pc->priority, pc->states);
}

/**********************************
Author: Chad

This command is used to display information about all
processes that are currently in the ready state or the
suspended-ready state. The display should show information
for all non- suspended processes, followed by all suspended ones.
Within each group, the processes should be presented in priority
order, that is, the same order in which they appear in the queue.
Besides the process name, this display should include at least
the priority value and suspended status.

!!!!!!!!
This is only error checking
!!!!!!!!
**********************************/

int showReady(int argc, char *argv[])
{
	struct PCB_t *pcbPtr1;
	struct PCB_t *pcbPtr2;
	pcbPtr1 = readyQueuePtr->head;
	pcbPtr2 = suspReadyQueuePtr->head;
	if(strcmp(argv[0], "showReady") != 0)
		return 0;
	else if(argc != 1)
	{
		printf("Invalid showReady command\n");
		return 1;
	}
	else if( pcbPtr1 == NULL && pcbPtr2 == NULL )
	{
		printf( "The ready queues are empty\n");
		return 1;
	}
	else
	{
		printf("ready queue:\n");
		while( pcbPtr1 != NULL )
		{
			showPCBs(pcbPtr1);
			pcbPtr1 = pcbPtr1->next;
		}
		printf("suspended ready queue\n");
		while( pcbPtr2 != NULL )
		{
			showPCBs(pcbPtr2);
			pcbPtr2 = pcbPtr2->next;
		}
		return 1;
	}
}


/**********************************
Author: Chad

This command is used to display information about all processes
which are currently in the blocked state or the suspended-blocked state.
The display should show information for all non- suspended processes,
followed by all suspended ones. Within each group, the processes
should be presented in the same order in which they appear in the queue.
Besides the process name, this display should include at least
the suspended status.

!!!!!!!!
This is only error checking
!!!!!!!!
**********************************/

int showBlocked(int argc, char *argv[]){
	struct PCB_t *pcbPtr1;
	struct PCB_t *pcbPtr2;
	pcbPtr1 = blockedQueuePtr->head;
	pcbPtr2 = suspBlockedQueuePtr->head;
	if(strcmp(argv[0], "showBlocked") != 0)
		return 0;
	else if(argc != 1)
	{
		printf("Invalid showReady command\n");
		return 1;
	}
	else if( pcbPtr1 == NULL && pcbPtr2 == NULL )
	{
		printf( "The ready queues are empty\n");
		return 1;
	}
	else
	{
		printf("blocked queue:\n");
		while( pcbPtr1 != NULL )
		{
			showPCBs(pcbPtr1);
			pcbPtr1 = pcbPtr1->next;
		}
		printf("suspended blocked queue\n");
		while( pcbPtr2 != NULL )
		{
			showPCBs(pcbPtr2);
			pcbPtr2 = pcbPtr2->next;
		}
		return 1;
	}
}

/**********************************
Author: Chad

This command is used to display information about all
PCBs that are currently in use. The PCBs may be listed
in any convenient order.
In this and the following commands that display information
about multiple PCBs, the information you display may be limited
or extensive. The required items for the Show All command are
the process name and state (including its suspended status).
Other information is optional. You must continue to ensure that
all information fits on a single screen, or that multiple screens c
an be handled.

!!!!!!!!
omg this is stupid
This is only error checking
!!!!!!!!
**********************************/

int showAll(int argc, char *argv[]){
	struct PCB_t *pcbPtr1;
	struct PCB_t *pcbPtr2;
	struct PCB_t *pcbPtr3;
	struct PCB_t *pcbPtr4;
	pcbPtr1 = blockedQueuePtr->head;
	pcbPtr2 = suspBlockedQueuePtr->head;
	pcbPtr3 = readyQueuePtr->head;
	pcbPtr4 = suspReadyQueuePtr->head;
	if(strcmp(argv[0], "showAll") != 0)
		return 0;
	else if(argc != 1)
	{
		printf("Invalid showAll command\n");
		return 1;
	}
	else
	{
		printf("blocked queue:\n");
		while( pcbPtr1 != NULL )
		{
			showPCBs(pcbPtr1);
			pcbPtr1 = pcbPtr1->next;
		}
		printf("suspended blocked queue\n");
		while( pcbPtr2 != NULL )
		{
			showPCBs(pcbPtr2);
			pcbPtr2 = pcbPtr2->next;
		}
		printf("ready queue:\n");
		while( pcbPtr3 != NULL )
		{
			showPCBs(pcbPtr3);
			pcbPtr3 = pcbPtr3->next;
		}
		printf("suspended ready queue\n");
		while( pcbPtr4 != NULL )
		{
			showPCBs(pcbPtr4);
			pcbPtr4 = pcbPtr4->next;
		}

		return 1;
	}
}

/************************************
Author:

This procedure removes a PCB from a specified queue. As in
the previous procedure, the PCB identifier and queue identifier
are provided as parameters. No additional parameters are required.
Suitable checks should be made, and a success or error code should
be returned.

!!!!!!!!!!
calling function needs error checking
!!!!!!!!!!
************************************/

int removePCB(PCB* a)
{
	if(findPCB(a))
		helperRemove(a);
		
	sys_free_mem(a->base);
	sys_free_mem(a);

	return 0;
}

int helperRemove( PCB* b )
{
	PCB_QUEUE* que = getQueue(b->states);
	if(que->count==1)
	{
		que->head = NULL;
		que->tail = NULL;
	}
	else if( b == que->head )
	{
	    que->head=que->head->next;
	    que->head->prev=NULL;
	}
	else if( b == que->tail )
	{
	    que->tail = que->tail->prev;
	    que->tail->next = NULL;
	}
	else
	{
		b->prev->next = b->next;
		b->next->prev = b->prev;
	}
	que->count--;
	b->next = NULL;
	b->prev = NULL;
	return 0;
}

/************************************
Author:Wisam
Edited: Brandon Strickland

This procedure should find or allocate an available PCB.
mark it as in use, and return a PCB pointer or identifier.
This identifier will be used as an input parameter to identify
this PCB in other procedures. No parameters are required for
Allocate-PCB. A null pointer should be returned if no PCB can be allocated.

This must use sys_mem_alloc for creation of stack.
************************************/

PCB *allocatePCB()
{
	PCB *b;
	b = (unsigned char*)sys_alloc_mem( sizeof(PCB) );
	b->base = (unsigned char*)sys_alloc_mem( 1024 );
	b->top = b->base + 1024;
	return b;
}

PCB_QUEUE* getQueue(int a)
{
	if( a == READY )
		return readyQueuePtr;
	else if( a == BLOCKED )
		return blockedQueuePtr;
	else if( a == SUSPENDED_READY )
		return suspReadyQueuePtr;
	else
		return suspBlockedQueuePtr;

}

PCB_QUEUE* getReadyQueue()
{
	return readyQueuePtr;
}
